/** DllImport - A semi-dynamic DLL/SO Wrapper
  Written in the D programming language 1.0

Authors:	Wei Li <oldrev@gmail.com>
Copyright:	Copyright (c) 2007 by Wei Li.
License:	BSD

*/

module dotmars.runtime.dll;

import dotmars.base.typetraits;
import dotmars.text.utf;
import dotmars.base.string;

import dotmars.platform.base;
import dotmars.base.stdtypes;
import dotmars.io.ioexcept;

version(Posix)
{
	import dotmars.platform.posix.dlfcn;
}



/** A Simple Wrapper of DLL/so
 * 
 *
 */
class Module : IDisposable
{

	version(Win32){
		alias HMODULE Handle;
		const Handle InvalidHandle = null;
	}//version(Win32)
	else version(Posix) {
		alias void*	Handle;
		const Handle InvalidHandle = null;
	}

	private Handle m_handle;

	debug {
		private void assertValid()
		{
			assert(m_handle != InvalidHandle);
		}
	}//debug

	/**
	 * Params:
	 * path = The path of the shared library
	 */
	public this(char[] path)
	in {
			assert(path !is null);
			assert(path.length > 0);
	}
	body {

		version(Win32)
			m_handle = LoadLibraryW(toUtf16z(path));  
		else version(Posix)
			m_handle = dlopen(toStringz(path), RTLD_NOW);

		if(m_handle is null)
			throw new FileNotFoundException("Failed to load dynamic library: " ~ path);
	}

	public ~this()
	{
		if(m_handle != InvalidHandle)close();
	}

	public void close()  
	{  
		if(m_handle != InvalidHandle)
		{
			version(Win32) 
				FreeLibrary(m_handle);  
			else version(Posix) 
				dlclose(m_handle);

			m_handle = InvalidHandle;
		}
	}  

	public T getSymbol(T)(char[] sym)  
	{  
		return cast(T)getSymbolAddress(sym);  
	}  

	public void* getSymbolAddress(char[] sym)  
	{  
		debug assertValid;

		version(Win32) 
			return GetProcAddress(m_handle, toStringz(sym));  
		else version(Posix) 
			return dlsym(m_handle, toStringz(sym));
	}  
}


private template MixinMembers(S, V...)  
{ 
	mixin("private alias S.FunctionType " ~ S.Name ~ "_t;");  
	mixin("S.FunctionType " ~ S.Name ~ ";");  

	static if(V.length > 0)  
		mixin MixinMembers!(V);  
}  


/** Define a symbol in the dynamic library.
 *
 * Params:
 * Sym = The name of the symbol which in the library
 * Func = The Prototype of the symbol
 * 
 * Examples:
 * Symbol!("dlclose", void function(int));
 */
struct Symbol(char[] Sym, Func)  
{  
	const char[] Name = Sym;  

	version(Windows){
		extern(Windows) alias ReturnType!(Func)   
			function(ParameterTypeTuple!(Func))     FunctionType;  

	}//version(Windows)

	version(Posix){

		extern(C) alias ReturnType!(Func)   
			function(ParameterTypeTuple!(Func))     FunctionType;  

	}//version(Posix);

}  


/** Provided a wrapper for semi-dynamic DLL
 *
 * Params:
 * Path = The path of the shared library
 * Symbols = Symbols in the shared library
 * Examples:
 * auto user32Dll =  
 * 	new DllImport!("user32.dll", Symbol!("MessageBoxW", uint function(HWND, LPWSTR, LPWSTR, DWORD);
 * user32dll.MessageBoxW(null, "Hello World!\0", "Hello\0", MB_OK); 
 */
class DllImport(char[] Path, Symbols...)  : IDisposable
{  
	private Module m_dll = null; 

	public mixin MixinMembers!(Symbols);  

	public this()  
	{ 
		m_dll = new Module(Path);
		initSymbols();  
	} 

	public ~this()
	{
		m_dll.close();
	}

	public override void close()
	{
		m_dll.close();
	}

	private void initSymbols()  
	{  
		foreach (S; Symbols)  
		{  
			mixin(S.Name ~ " = m_dll.getSymbol!(" ~ S.Name ~ "_t)(S.Name);");  
		}  
	}  
}  
